home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / msdos.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  19KB  |  1,008 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * msdos.c
  11.  *
  12.  * MSDOS system-dependent routines.
  13.  * A cheap plastic imitation of the amiga dependent code.
  14.  * A lot in this file was made by Juergen Weigert (jw).
  15.  */
  16.  
  17. #include <io.h>
  18. #include "vim.h"
  19. #include "globals.h"
  20. #include "param.h"
  21. #include "proto.h"
  22. #include <conio.h>
  23. #include <fcntl.h>
  24. #include <bios.h>
  25. #include <alloc.h>
  26.  
  27. static int WaitForChar __ARGS((int));
  28. static int change_drive __ARGS((int));
  29. static int cbrk_handler __ARGS(());
  30.  
  31. typedef struct filelist
  32. {
  33.     char_u    **file;
  34.     int        nfiles;
  35.     int        maxfiles;
  36. } FileList;
  37.  
  38. static void        addfile __ARGS((FileList *, char_u *, int));
  39. static int        pstrcmp();    /* __ARGS((char **, char **)); BCC does not like this */
  40. static void        strlowcpy __ARGS((char_u *, char_u *));
  41. static int        expandpath __ARGS((FileList *, char_u *, int, int, int));
  42.  
  43. static int cbrk_pressed = FALSE;    /* set by ctrl-break interrupt */
  44. static int ctrlc_pressed = FALSE;    /* set when ctrl-C or ctrl-break detected */
  45. static int delayed_redraw = FALSE;    /* set when ctrl-C detected */
  46.  
  47.     long
  48. mch_avail_mem(special)
  49.     int special;
  50. {
  51.     return coreleft();
  52. }
  53.  
  54.     void
  55. vim_delay()
  56. {
  57.     delay(500);
  58. }
  59.  
  60. /*
  61.  * this version of remove is not scared by a readonly (backup) file
  62.  *
  63.  * returns -1 on error, 0 otherwise (just like remove())
  64.  */
  65.     int
  66. vim_remove(name)
  67.     char_u *name;
  68. {
  69.     (void)setperm(name, 0);    /* default permissions */
  70.     return unlink(name);
  71. }
  72.  
  73. /*
  74.  * mch_write(): write the output buffer to the screen
  75.  */
  76.     void
  77. mch_write(s, len)
  78.     char_u    *s;
  79.     int        len;
  80. {
  81.     char_u    *p;
  82.     int        row, col;
  83.  
  84.     if (term_console)        /* translate ESC | sequences into bios calls */
  85.         while (len--)
  86.         {
  87.             if (s[0] == '\n')
  88.                 putch('\r');
  89.             else if (s[0] == ESC && len > 1 && s[1] == '|')
  90.             {
  91.                 switch (s[2])
  92.                 {
  93.                 case 'J':    clrscr();
  94.                             goto got3;
  95.  
  96.                 case 'K':    clreol();
  97.                             goto got3;
  98.  
  99.                 case 'L':    insline();
  100.                             goto got3;
  101.  
  102.                 case 'M':    delline();
  103. got3:                        s += 3;
  104.                             len -= 2;
  105.                             continue;
  106.  
  107.                 case '0':
  108.                 case '1':
  109.                 case '2':
  110.                 case '3':
  111.                 case '4':
  112.                 case '5':
  113.                 case '6':
  114.                 case '7':
  115.                 case '8':
  116.                 case '9':    p = s + 2;
  117.                             row = getdigits(&p);        /* no check for length! */
  118.                             if (p > s + len)
  119.                                 break;
  120.                             if (*p == ';')
  121.                             {
  122.                                 ++p;
  123.                                 col = getdigits(&p);    /* no check for length! */
  124.                                 if (p > s + len)
  125.                                     break;
  126.                                 if (*p == 'H' || *p == 'r')
  127.                                 {
  128.                                     if (*p == 'H')        /* set cursor position */
  129.                                         gotoxy(col, row);
  130.                                     else                /* set scroll region  */
  131.                                         window(1, row, Columns, col);
  132.                                     len -= p - s;
  133.                                     s = p + 1;
  134.                                     continue;
  135.                                 }
  136.                             }
  137.                             else if (*p == 'm')
  138.                             {
  139.                                 if (row == 0)
  140.                                     normvideo();
  141.                                 else
  142.                                     textattr(row);
  143.                                 len -= p - s;
  144.                                 s = p + 1;
  145.                                 continue;
  146.                             }
  147.                 }
  148.             }
  149.             putch(*s++);
  150.         }
  151.     else
  152.         write(1, s, (unsigned)len);
  153. }
  154.  
  155. #define POLL_SPEED 10    /* milliseconds between polls */
  156.  
  157. /*
  158.  * Simulate WaitForChar() by slowly polling with bioskey(1) or kbhit().
  159.  *
  160.  * If Vim should work over the serial line after a 'ctty com1' we must use
  161.  * kbhit() and getch(). (jw)
  162.  * Usually kbhit() is not used, because then CTRL-C and CTRL-P
  163.  * will be catched by DOS (mool).
  164.  *
  165.  * return TRUE if a character is available, FALSE otherwise
  166.  */
  167.  
  168.     static int
  169. WaitForChar(msec)
  170.     int msec;
  171. {
  172.     for (;;)
  173.     {
  174.         if ((p_biosk ? bioskey(1) : kbhit()) || cbrk_pressed)
  175.             return TRUE;
  176.         if (msec <= 0)
  177.             break;
  178.         delay(POLL_SPEED);
  179.         msec -= POLL_SPEED;
  180.     }
  181.     return FALSE;
  182. }
  183.  
  184. /*
  185.  * GetChars(): low level input funcion.
  186.  * Get a characters from the keyboard.
  187.  * If time == 0 do not wait for characters.
  188.  * If time == n wait a short time for characters.
  189.  * If time == -1 wait forever for characters.
  190.  *
  191.  * return the number of characters obtained
  192.  */
  193.     int
  194. GetChars(buf, maxlen, time)
  195.     char_u        *buf;
  196.     int         maxlen;
  197.     int         time;
  198. {
  199.     int         len = 0;
  200.     int            c;
  201.  
  202. /*
  203.  * if we got a ctrl-C when we were busy, there will be a "^C" somewhere
  204.  * on the sceen, so we need to redisplay it.
  205.  */
  206.     if (delayed_redraw)
  207.     {
  208.         delayed_redraw = FALSE;
  209.         updateScreen(CLEAR);
  210.         setcursor();
  211.         flushbuf();
  212.     }
  213.  
  214.     if (time >= 0)
  215.     {
  216.         if (WaitForChar(time) == 0)     /* no character available */
  217.             return 0;
  218.     }
  219.     else    /* time == -1 */
  220.     {
  221.     /*
  222.      * If there is no character available within 2 seconds (default)
  223.      * write the autoscript file to disk
  224.      */
  225.         if (WaitForChar((int)p_ut) == 0)
  226.             updatescript(0);
  227.     }
  228.  
  229. /*
  230.  * Try to read as many characters as there are.
  231.  * Works for the controlling tty only.
  232.  */
  233.     --maxlen;        /* may get two chars at once */
  234.     /*
  235.      * we will get at least one key. Get more if they are available
  236.      * After a ctrl-break we have to read a 0 (!) from the buffer.
  237.      * bioskey(1) will return 0 if no key is available and when a
  238.      * ctrl-break was typed. When ctrl-break is hit, this does not always
  239.      * implies a key hit.
  240.      */
  241.     cbrk_pressed = FALSE;
  242.     if (p_biosk)
  243.         while ((len == 0 || bioskey(1)) && len < maxlen)
  244.         {
  245.             c = bioskey(0);            /* get the key */
  246.             if (c == 0)                /* ctrl-break */
  247.                 c = 3;                /* return a CTRL-C */
  248.             if ((c & 0xff) == 0)
  249.             {
  250.                 if (c == 0x0300)        /* CTRL-@ is 0x0300, translated into K_ZERO */
  251.                     c = K_ZERO;
  252.                 else        /* extended key code 0xnn00 translated into K_NUL, nn */
  253.                 {
  254.                     c >>= 8;
  255.                     *buf++ = K_NUL;
  256.                     ++len;
  257.                 }
  258.             }
  259.  
  260.             *buf++ = c;
  261.             len++;
  262.         }
  263.     else
  264.         while ((len == 0 || kbhit()) && len < maxlen)
  265.         {
  266.             switch (c = getch())
  267.             {
  268.             case 0:
  269.                     *buf++ = K_NUL;
  270.                     break;
  271.             case 3:
  272.                     cbrk_pressed = TRUE;
  273.                     /*FALLTHROUGH*/
  274.             default:
  275.                     *buf++ = c;
  276.             }
  277.             len++;
  278.         }
  279.     return len;
  280. }
  281.  
  282. /*
  283.  * return non-zero if a character is available
  284.  */
  285.     int
  286. mch_char_avail()
  287. {
  288.     return WaitForChar(0);
  289. }
  290.  
  291. /*
  292.  * We have no job control, fake it by starting a new shell.
  293.  */
  294.     void
  295. mch_suspend()
  296. {
  297.     OUTSTR("new shell started\n");
  298.     (void)call_shell(NULL, 0, TRUE);
  299. }
  300.  
  301. extern int _fmode;
  302. /*
  303.  * we do not use windows, there is not much to do here
  304.  */
  305.     void
  306. mch_windinit()
  307. {
  308.     _fmode = O_BINARY;        /* we do our own CR-LF translation */
  309.     flushbuf();
  310.     (void)mch_get_winsize();
  311. }
  312.  
  313.     void
  314. check_win(argc, argv)
  315.     int        argc;
  316.     char    **argv;
  317. {
  318.     if (!isatty(0) || !isatty(1))
  319.     {
  320.         fprintf(stderr, "VIM: no controlling terminal\n");
  321.         exit(2);
  322.     }
  323.     /*
  324.      * In some cases with DOS 6.0 on a NEC notebook there is a 12 seconds
  325.      * delay when starting up that can be avoided by the next two lines.
  326.      * Don't ask me why!
  327.      * This could be fixed by removing setver.sys from config.sys. Forget it.
  328.     gotoxy(1,1);
  329.     cputs(" ");
  330.      */
  331. }
  332.  
  333. /*
  334.  * fname_case(): Set the case of the filename, if it already exists.
  335.  *                 msdos filesystem is far to primitive for that. do nothing.
  336.  */
  337.     void
  338. fname_case(name)
  339.     char_u *name;
  340. {
  341. }
  342.  
  343. /*
  344.  * mch_settitle(): set titlebar of our window.
  345.  * Dos console has no title.
  346.  */
  347.     void
  348. mch_settitle(title, icon)
  349.     char_u *title;
  350.     char_u *icon;
  351. {
  352. }
  353.  
  354. /*
  355.  * Restore the window/icon title. (which we don't have)
  356.  */
  357.     void
  358. mch_restore_title(which)
  359.     int which;
  360. {
  361. }
  362.  
  363. /*
  364.  * Get name of current directory into buffer 'buf' of length 'len' bytes.
  365.  * Return OK for success, FAIL for failure.
  366.  */
  367.     int
  368. vim_dirname(buf, len)
  369.     char_u    *buf;
  370.     int        len;
  371. {
  372.     return (getcwd(buf, len) != NULL ? OK : FAIL);
  373. }
  374.  
  375. /*
  376.  * Change default drive (just like _chdrive of Borland C 3.1)
  377.  */
  378.     static int
  379. change_drive(drive)
  380.     int drive;
  381. {
  382.     unsigned dummy;
  383.     union REGS regs;
  384.  
  385.     regs.h.ah = 0x0e;
  386.     regs.h.dl = drive - 1;
  387.     intdos(®s, ®s);    /* set default drive */
  388.     regs.h.ah = 0x19;
  389.     intdos(®s, ®s);    /* get default drive */
  390.     if (regs.h.al == drive - 1)
  391.         return 0;
  392.     else
  393.         return -1;
  394. }
  395.  
  396. /*
  397.  * get absolute filename into buffer 'buf' of length 'len' bytes
  398.  *
  399.  * return FAIL for failure, OK otherwise
  400.  */
  401.     int
  402. FullName(fname, buf, len)
  403.     char_u    *fname, *buf;
  404.     int        len;
  405. {
  406.     if (fname == NULL)    /* always fail */
  407.     {
  408.         *buf = NUL;
  409.         return FAIL;
  410.     }
  411.  
  412.     if (isFullName(fname))        /* allready expanded */
  413.     {
  414.         STRNCPY(buf, fname, len);
  415.         return OK;
  416.     }
  417.  
  418. #ifdef __BORLANDC__        /* the old Turbo C does not have this */
  419.     if (_fullpath(buf, fname, len) == NULL)
  420.     {
  421.         STRNCPY(buf, fname, len);    /* failed, use the relative path name */
  422.         return FAIL;
  423.     }
  424.     return OK;
  425. #else                    /* almost the same as FullName in unix.c */
  426.     {
  427.         int        l;
  428.         char_u    olddir[MAXPATHL];
  429.         char_u    *p, *q;
  430.         int        c;
  431.         int        retval = OK;
  432.  
  433.         *buf = 0;
  434.         /*
  435.          * change to the directory for a moment,
  436.          * and then do the getwd() (and get back to where we were).
  437.          * This will get the correct path name with "../" things.
  438.          */
  439.         p = strrchr(fname, '/');
  440.         q = strrchr(fname, '\\');
  441.         if (q && (p == NULL || q > p))
  442.             p = q;
  443.         q = strrchr(fname, ':');
  444.         if (q && (p == NULL || q > p))
  445.             p = q;
  446.         if (p != NULL)
  447.         {
  448.             if (getcwd(olddir, MAXPATHL) == NULL)
  449.             {
  450.                 p = NULL;        /* can't get current dir: don't chdir */
  451.                 retval = FAIL;
  452.             }
  453.             else
  454.             {
  455.                 if (*p == ':' || (p > fname && p[-1] == ':'))
  456.                     q = p + 1;
  457.                 else
  458.                     q = p;
  459.                 c = *q;
  460.                 *q = NUL;
  461.                 if (chdir(fname))
  462.                     retval = FAIL;
  463.                 else
  464.                     fname = p + 1;
  465.                 *q = c;
  466.             }
  467.         }
  468.         if (getcwd(buf, len) == NULL)
  469.         {
  470.             retval = FAIL;
  471.             *buf = NUL;
  472.         }
  473.         l = STRLEN(buf);
  474.         if (l && buf[l - 1] != '/' && buf[l - 1] != '\\')
  475.             strcat(buf, "\\");
  476.         if (p)
  477.             chdir(olddir);
  478.         strcat(buf, fname);
  479.         return retval;
  480.     }
  481. #endif
  482. }
  483.  
  484. /*
  485.  * return TRUE is fname is an absolute path name
  486.  */
  487.     int
  488. isFullName(fname)
  489.     char_u        *fname;
  490. {
  491.     return (STRCHR(fname, ':') != NULL);
  492. }
  493.  
  494. /*
  495.  * get file permissions for 'name'
  496.  * -1 : error
  497.  * else FA_attributes defined in dos.h
  498.  */
  499.     long
  500. getperm(name)
  501.     char_u *name;
  502. {
  503.     int r;
  504.  
  505.     r = _chmod(name, 0, 0);         /* get file mode */
  506.     return r;
  507. }
  508.  
  509. /*
  510.  * set file permission for 'name' to 'perm'
  511.  *
  512.  * return FAIL for failure, OK otherwise
  513.  */
  514.     int
  515. setperm(name, perm)
  516.     char_u    *name;
  517.     long    perm;
  518. {
  519.     perm |= FA_ARCH;        /* file has changed, set archive bit */
  520.     return (_chmod((char *)name, 1, (int)perm) == -1 ? FAIL : OK);
  521. }
  522.  
  523. /*
  524.  * return TRUE if "name" is a directory
  525.  * return FALSE if "name" is not a directory
  526.  * return -1 for error
  527.  *
  528.  * beware of a trailing backslash that may have been added by addfile()
  529.  */
  530.     int
  531. isdir(name)
  532.     char_u *name;
  533. {
  534.     int        f;
  535.     char_u    *p;
  536.  
  537.     p = name + strlen(name);
  538.     if (p > name)
  539.         --p;
  540.     if (*p == '\\')                    /* remove trailing backslash for a moment */
  541.         *p = NUL;
  542.     else
  543.         p = NULL;
  544.     f = _chmod(name, 0, 0);
  545.     if (p != NULL)
  546.         *p = '\\';
  547.     if (f == -1)
  548.         return -1;                    /* file does not exist at all */
  549.     if ((f & FA_DIREC) == 0)
  550.         return FALSE;                /* not a directory */
  551.     return TRUE;
  552. }
  553.  
  554. /*
  555.  * Careful: mch_windexit() may be called before mch_windinit()!
  556.  */
  557.     void
  558. mch_windexit(r)
  559.     int r;
  560. {
  561.     settmode(0);
  562.     stoptermcap();
  563.     flushbuf();
  564.     ml_close_all();                 /* remove all memfiles */
  565.     exit(r);
  566. }
  567.  
  568. /*
  569.  * function for ctrl-break interrupt
  570.  */
  571.     void interrupt
  572. catch_cbrk()
  573. {
  574.     cbrk_pressed = TRUE;
  575.     ctrlc_pressed = TRUE;
  576. }
  577.  
  578. /*
  579.  * ctrl-break handler for DOS. Never called when a ctrl-break is typed, because
  580.  * we catch interrupt 1b. If you type ctrl-C while Vim is waiting for a
  581.  * character this function is not called. When a ctrl-C is typed while Vim is
  582.  * busy this function may be called. By that time a ^C has been displayed on
  583.  * the screen, so we have to redisplay the screen. We can't do that here,
  584.  * because we may be called by DOS. The redraw is in GetChars().
  585.  */
  586.     static int
  587. cbrk_handler()
  588. {
  589.     delayed_redraw = TRUE;
  590.     return 1;                 /* resume operation after ctrl-break */
  591. }
  592.  
  593. /*
  594.  * function for critical error interrupt
  595.  * For DOS 1 and 2 return 0 (Ignore).
  596.  * For DOS 3 and later return 3 (Fail)
  597.  */
  598.     void interrupt
  599. catch_cint(bp, di, si, ds, es, dx, cx, bx, ax)
  600.     unsigned bp, di, si, ds, es, dx, cx, bx, ax;
  601. {
  602.     ax = (ax & 0xff00);        /* set AL to 0 */
  603.     if (_osmajor >= 3)
  604.         ax |= 3;            /* set AL to 3 */
  605. }
  606.  
  607. /*
  608.  * set the tty in (raw) ? "raw" : "cooked" mode
  609.  *
  610.  * Does not change the tty, as bioskey() and kbhit() work raw all the time.
  611.  */
  612.  
  613. extern void interrupt CINT_FUNC();
  614.  
  615.     void
  616. mch_settmode(raw)
  617.     int  raw;
  618. {
  619.     static int saved_cbrk;
  620.     static void interrupt (*old_cint)();
  621.     static void interrupt (*old_cbrk)();
  622.  
  623.     if (raw)
  624.     {
  625.         saved_cbrk = getcbrk();            /* save old ctrl-break setting */
  626.         setcbrk(0);                        /* do not check for ctrl-break */
  627.         old_cint = getvect(0x24);         /* save old critical error interrupt */
  628.         setvect(0x24, catch_cint);        /* install our critical error interrupt */
  629.         old_cbrk = getvect(0x1B);         /* save old ctrl-break interrupt */
  630.         setvect(0x1B, catch_cbrk);        /* install our ctrl-break interrupt */
  631.         ctrlbrk(cbrk_handler);            /* vim's ctrl-break handler */
  632.         if (term_console)
  633.             outstr(T_TP);                /* set colors */
  634.     }
  635.     else
  636.     {
  637.         setcbrk(saved_cbrk);            /* restore ctrl-break setting */
  638.         setvect(0x24, old_cint);        /* restore critical error interrupt */
  639.         setvect(0x1B, old_cbrk);        /* restore ctrl-break interrupt */
  640.         /* restore ctrl-break handler, how ??? */
  641.         if (term_console)
  642.             normvideo();                /* restore screen colors */
  643.     }
  644. }
  645.  
  646. /*
  647.  * set screen mode
  648.  * return FAIL for failure, OK otherwise
  649.  */
  650.     int
  651. mch_screenmode(arg)
  652.     char_u        *arg;
  653. {
  654.     int                mode;
  655.     int                i;
  656.     static char_u *(names[]) = {"BW40", "C40", "BW80", "C80", "MONO", "C4350"};
  657.     static int        modes[]  = { BW40,   C40,   BW80,   C80,   MONO,   C4350};
  658.  
  659.     mode = -1;
  660.     if (isdigit(*arg))                /* mode number given */
  661.         mode = atoi((char *)arg);
  662.     else
  663.     {
  664.         for (i = 0; i < sizeof(names) / sizeof(char_u *); ++i)
  665.             if (stricmp((char *)names[i], (char *)arg) == 0)
  666.             {
  667.                 mode = modes[i];
  668.                 break;
  669.             }
  670.     }
  671.     if (mode == -1)
  672.     {
  673.         EMSG("Unsupported screen mode");
  674.         return FAIL;
  675.     }
  676.     textmode(mode);                    /* use Borland function */
  677.     return OK;
  678. }
  679.  
  680. /*
  681.  * Structure used by Turbo-C/Borland-C to store video parameters.
  682.  */
  683. extern struct text_info _video;
  684.  
  685. /*
  686.  * try to get the real window size
  687.  * return FAIL for failure, OK otherwise
  688.  */
  689.     int
  690. mch_get_winsize()
  691. {
  692.     int i;
  693.     struct text_info ti;
  694. /*
  695.  * The screenwidth is returned by the BIOS OK.
  696.  * The screenheight is in a location in the bios RAM, if the display is EGA or VGA.
  697.  */
  698.     if (!term_console)
  699.         return FAIL;
  700.     gettextinfo(&ti);
  701.     Columns = ti.screenwidth;
  702.     Rows = ti.screenheight;
  703.     if (ti.currmode > 10)
  704.         Rows = *(char far *)MK_FP(0x40, 0x84) + 1;
  705.     set_window();
  706.  
  707.     if (Columns < 5 || Columns > MAX_COLUMNS ||
  708.                     Rows < 2 || Rows > MAX_COLUMNS)
  709.     {
  710.         /* these values are overwritten by termcap size or default */
  711.         Columns = 80;
  712.         Rows = 25;
  713.         return FAIL;
  714.     }
  715.     check_winsize();
  716.  
  717.     return OK;
  718. }
  719.  
  720. /*
  721.  * Set the active window for delline/insline.
  722.  */
  723.     void
  724. set_window()
  725. {
  726.     _video.screenheight = Rows;
  727.     window(1, 1, Columns, Rows);
  728. }
  729.  
  730.     void
  731. mch_set_winsize()
  732. {
  733.     /* should try to set the window size to Rows and Columns */
  734.     /* may involve switching display mode.... */
  735. }
  736.  
  737. /*
  738.  * call shell, return FAIL for failure, OK otherwise
  739.  */
  740.     int
  741. call_shell(cmd, filter, cooked)
  742.     char_u    *cmd;
  743.     int     filter;         /* if != 0: called by dofilter() */
  744.     int        cooked;
  745. {
  746.     int        x;
  747.     char_u    newcmd[200];
  748.  
  749.     flushbuf();
  750.  
  751.     if (cooked)
  752.         settmode(0);        /* set to cooked mode */
  753.  
  754.     if (cmd == NULL)
  755.         x = system(p_sh);
  756.     else
  757.     {                     /* we use "command" to start the shell, slow but easy */
  758.         sprintf(newcmd, "%s /c %s", p_sh, cmd);
  759.         x = system(newcmd);
  760.     }
  761.     outchar('\n');
  762.     if (cooked)
  763.         settmode(1);        /* set to raw mode */
  764.  
  765. #ifdef WEBB_COMPLETE
  766.     if (x && !expand_interactively)
  767. #else
  768.     if (x)
  769. #endif
  770.     {
  771.         outnum((long)x);
  772.         outstrn((char_u *)" returned\n");
  773.     }
  774.  
  775.     resettitle();
  776.     (void)mch_get_winsize();        /* display mode may have been changed */
  777.     return (x ? FAIL : OK);
  778. }
  779.  
  780. /*
  781.  * check for an "interrupt signal": CTRL-break or CTRL-C
  782.  */
  783.     void
  784. breakcheck()
  785. {
  786.     if (ctrlc_pressed)
  787.     {
  788.         ctrlc_pressed = FALSE;
  789.         got_int = TRUE;
  790.     }
  791. }
  792.  
  793. #define FL_CHUNK 32
  794.  
  795.     static void
  796. addfile(fl, f, isdir)
  797.     FileList    *fl;
  798.     char_u        *f;
  799.     int            isdir;
  800. {
  801.     char_u        *p;
  802.  
  803.     if (!fl->file)
  804.     {
  805.         fl->file = (char_u **)alloc(sizeof(char_u *) * FL_CHUNK);
  806.         if (!fl->file)
  807.             return;
  808.         fl->nfiles = 0;
  809.         fl->maxfiles = FL_CHUNK;
  810.     }
  811.     if (fl->nfiles >= fl->maxfiles)
  812.     {
  813.         char_u    **t;
  814.         int        i;
  815.  
  816.         t = (char_u **)lalloc((long_u)(sizeof(char_u *) * (fl->maxfiles + FL_CHUNK)), TRUE);
  817.         if (!t)
  818.             return;
  819.         for (i = fl->nfiles - 1; i >= 0; i--)
  820.             t[i] = fl->file[i];
  821.         free(fl->file);
  822.         fl->file = t;
  823.         fl->maxfiles += FL_CHUNK;
  824.     }
  825.     p = alloc((unsigned)(STRLEN(f) + 1 + isdir));
  826.     if (p)
  827.     {
  828.         STRCPY(p, f);
  829.         if (isdir)
  830.             strcat(p, "\\");
  831.     }
  832.     fl->file[fl->nfiles++] = p;
  833. }
  834.  
  835.     static int
  836. pstrcmp(a, b)
  837.     char_u **a, **b;
  838. {
  839.     return (strcmp(*a, *b));
  840. }
  841.  
  842.     int
  843. has_wildcard(s)
  844.     char_u *s;
  845. {
  846.     if (s)
  847.         for ( ; *s; ++s)
  848.             if (*s == '?' || *s == '*')
  849.                 return TRUE;
  850.     return FALSE;
  851. }
  852.  
  853.     static void
  854. strlowcpy(d, s)
  855.     char_u *d, *s;
  856. {
  857.     while (*s)
  858.         *d++ = tolower(*s++);
  859.     *d = '\0';
  860. }
  861.  
  862.     static int
  863. expandpath(fl, path, fonly, donly, notf)
  864.     FileList    *fl;
  865.     char_u        *path;
  866.     int            fonly, donly, notf;
  867. {
  868.     char_u    buf[MAXPATH];
  869.     char_u    *p, *s, *e;
  870.     int        lastn, c, r;
  871.     struct    ffblk fb;
  872.  
  873.     lastn = fl->nfiles;
  874.  
  875. /*
  876.  * Find the first part in the path name that contains a wildcard.
  877.  * Copy it into buf, including the preceding characters.
  878.  */
  879.     p = buf;
  880.     s = NULL;
  881.     e = NULL;
  882.     while (*path)
  883.     {
  884.         if (*path == '\\' || *path == ':' || *path == '/')
  885.         {
  886.             if (e)
  887.                 break;
  888.             else
  889.                 s = p;
  890.         }
  891.         if (*path == '*' || *path == '?')
  892.             e = p;
  893.         *p++ = *path++;
  894.     }
  895.     e = p;
  896.     if (s)
  897.         s++;
  898.     else
  899.         s = buf;
  900.  
  901.     /* if the file name ends in "*" and does not contain a ".", addd ".*" */
  902.     if (e[-1] == '*' && STRCHR(s, '.') == NULL)
  903.     {
  904.         *e++ = '.';
  905.         *e++ = '*';
  906.     }
  907.     /* now we have one wildcard component between s and e */
  908.     *e = '\0';
  909.     r = 0;
  910.     /* If we are expanding wildcards we try both files and directories */
  911.     if ((c = findfirst(buf, &fb, (*path || !notf) ? FA_DIREC : 0)) != 0)
  912.     {
  913.         /* not found */
  914.         STRCPY(e, path);
  915.         if (notf)
  916.             addfile(fl, buf, FALSE);
  917.         return 1; /* unexpanded or empty */
  918.     }
  919.     while (!c)
  920.     {
  921.         strlowcpy(s, fb.ff_name);
  922.             /* ignore "." and ".." */
  923.         if (*s != '.' || (s[1] != '\0' && (s[1] != '.' || s[2] != '\0')))
  924.         {
  925.             strcat(buf, path);
  926.             if (!has_wildcard(path))
  927.                 addfile(fl, buf, (isdir(buf) == TRUE));
  928.             else
  929.                 r |= expandpath(fl, buf, fonly, donly, notf);
  930.         }
  931.         c = findnext(&fb);
  932.     }
  933.     qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char_u *), pstrcmp);
  934.     return r;
  935. }
  936.  
  937. /*
  938.  * MSDOS rebuilt of Scott Ballantynes ExpandWildCard for amiga/arp.
  939.  * jw
  940.  */
  941.  
  942.     int
  943. ExpandWildCards(num_pat, pat, num_file, file, files_only, list_notfound)
  944.     int     num_pat;
  945.     char_u    **pat;
  946.     int     *num_file;
  947.     char_u    ***file;
  948.     int     files_only, list_notfound;
  949. {
  950.     int            i, r = 0;
  951.     FileList    f;
  952.  
  953.     f.file = NULL;
  954.     f.nfiles = 0;
  955.     for (i = 0; i < num_pat; i++)
  956.     {
  957.         if (!has_wildcard(pat[i]))
  958.             addfile(&f, pat[i], files_only ? FALSE : (isdir(pat[i]) == TRUE));
  959.         else
  960.             r |= expandpath(&f, pat[i], files_only, 0, list_notfound);
  961.     }
  962.     if (r == 0)
  963.     {
  964.         *num_file = f.nfiles;
  965.         *file = f.file;
  966.     }
  967.     else
  968.     {
  969.         *num_file = 0;
  970.         *file = NULL;
  971.     }
  972.     return (r ? FAIL : OK);
  973. }
  974.  
  975.     void
  976. FreeWild(num, file)
  977.     int        num;
  978.     char_u    **file;
  979. {
  980.     if (file == NULL || num <= 0)
  981.         return;
  982.     while (num--)
  983.         free(file[num]);
  984.     free(file);
  985. }
  986.  
  987. /*
  988.  * The normal chdir() does not change the default drive.
  989.  * This one does.
  990.  */
  991. #undef chdir
  992.     int
  993. vim_chdir(path)
  994.     char_u *path;
  995. {
  996.     if (path[0] == NUL)                /* just checking... */
  997.         return 0;
  998.     if (path[1] == ':')                /* has a drive name */
  999.     {
  1000.         if (change_drive(toupper(path[0]) - 'A' + 1))
  1001.             return -1;                /* invalid drive name */
  1002.         path += 2;
  1003.     }
  1004.     if (*path == NUL)                /* drive name only */
  1005.         return 0;
  1006.     return chdir(path);                /* let the normal chdir() do the rest */
  1007. }
  1008.